From 633da67231ceaa9676f52c83034e3434bdf5c08d Mon Sep 17 00:00:00 2001 From: "awilliam@xenbuild.aw" Date: Fri, 21 Apr 2006 09:03:19 -0600 Subject: [PATCH] [IA64] Add support to physdev_ops Add support to physdev ops, and thus give IOSAPIC RTEs managed by Xen now. Dom0 now issues hypercall to r/w RTE entry. Another change is the irq vector allocation which is also owned by xen now. After this change, the IOSAPIC is almost owned by xen with only exception as IOSAPIC EOI which is still issued by dom0 directly. But that's OK since currently dom0 owns all external physical devices. Later full event channel mechanism will provide necessary support for driver domain, and at that time, dom0 instead issues physdev_op (PHYSDEVOP_IRQ_UNMASK_NOTIFY) naturally as replace of IOSAPIC EOI. Signed-off-by Kevin Tian --- .../arch/ia64/kernel/iosapic.c | 66 +++++++++++ .../arch/ia64/kernel/irq_ia64.c | 5 + .../include/asm-ia64/iosapic.h | 2 + xen/arch/ia64/linux-xen/iosapic.c | 111 ++++++++++++++++++ xen/arch/ia64/linux-xen/irq_ia64.c | 4 + xen/arch/ia64/xen/acpi.c | 16 +-- xen/arch/ia64/xen/hypercall.c | 88 +++++++++++++- xen/arch/ia64/xen/irq.c | 8 +- xen/include/asm-ia64/linux-xen/asm/iosapic.h | 28 ++++- 9 files changed, 304 insertions(+), 24 deletions(-) diff --git a/linux-2.6-xen-sparse/arch/ia64/kernel/iosapic.c b/linux-2.6-xen-sparse/arch/ia64/kernel/iosapic.c index 574084f343..8fba804bbe 100644 --- a/linux-2.6-xen-sparse/arch/ia64/kernel/iosapic.c +++ b/linux-2.6-xen-sparse/arch/ia64/kernel/iosapic.c @@ -140,6 +140,68 @@ static unsigned char pcat_compat __devinitdata; /* 8259 compatibility flag */ static int iosapic_kmalloc_ok; static LIST_HEAD(free_rte_list); +#ifdef CONFIG_XEN +#include +#include +#include +static inline unsigned int xen_iosapic_read(char __iomem *iosapic, unsigned int reg) +{ + physdev_op_t op; + int ret; + + op.cmd = PHYSDEVOP_APIC_READ; + op.u.apic_op.apic_physbase = (unsigned long)iosapic - + __IA64_UNCACHED_OFFSET; + op.u.apic_op.reg = reg; + ret = HYPERVISOR_physdev_op(&op); + if (ret) + return ret; + return op.u.apic_op.value; +} + +static inline void xen_iosapic_write(char __iomem *iosapic, unsigned int reg, u32 val) +{ + physdev_op_t op; + + op.cmd = PHYSDEVOP_APIC_WRITE; + op.u.apic_op.apic_physbase = (unsigned long)iosapic - + __IA64_UNCACHED_OFFSET; + op.u.apic_op.reg = reg; + op.u.apic_op.value = val; + HYPERVISOR_physdev_op(&op); +} + +static inline unsigned int iosapic_read(char __iomem *iosapic, unsigned int reg) +{ + if (!running_on_xen) { + writel(reg, iosapic + IOSAPIC_REG_SELECT); + return readl(iosapic + IOSAPIC_WINDOW); + } else + return xen_iosapic_read(iosapic, reg); +} + +static inline void iosapic_write(char __iomem *iosapic, unsigned int reg, u32 val) +{ + if (!running_on_xen) { + writel(reg, iosapic + IOSAPIC_REG_SELECT); + writel(val, iosapic + IOSAPIC_WINDOW); + } else + xen_iosapic_write(iosapic, reg, val); +} + +int xen_assign_irq_vector(int irq) +{ + physdev_op_t op; + + op.cmd = PHYSDEVOP_ASSIGN_VECTOR; + op.u.irq_op.irq = irq; + if (HYPERVISOR_physdev_op(&op)) + return -ENOSPC; + + return op.u.irq_op.vector; +} +#endif /* XEN */ + /* * Find an IOSAPIC associated with a GSI */ @@ -953,6 +1015,10 @@ iosapic_system_init (int system_pcat_compat) } pcat_compat = system_pcat_compat; +#ifdef CONFIG_XEN + if (running_on_xen) + return; +#endif if (pcat_compat) { /* * Disable the compatibility mode interrupts (8259 style), needs IN/OUT support diff --git a/linux-2.6-xen-sparse/arch/ia64/kernel/irq_ia64.c b/linux-2.6-xen-sparse/arch/ia64/kernel/irq_ia64.c index 6c4d59fd03..647b0ca608 100644 --- a/linux-2.6-xen-sparse/arch/ia64/kernel/irq_ia64.c +++ b/linux-2.6-xen-sparse/arch/ia64/kernel/irq_ia64.c @@ -66,6 +66,11 @@ int assign_irq_vector (int irq) { int pos, vector; +#ifdef CONFIG_XEN + extern int xen_assign_irq_vector(int); + if (running_on_xen) + return xen_assign_irq_vector(irq); +#endif /* CONFIG_XEN */ again: pos = find_first_zero_bit(ia64_vector_mask, IA64_NUM_DEVICE_VECTORS); vector = IA64_FIRST_DEVICE_VECTOR + pos; diff --git a/linux-2.6-xen-sparse/include/asm-ia64/iosapic.h b/linux-2.6-xen-sparse/include/asm-ia64/iosapic.h index 20f98f1751..87de4873f5 100644 --- a/linux-2.6-xen-sparse/include/asm-ia64/iosapic.h +++ b/linux-2.6-xen-sparse/include/asm-ia64/iosapic.h @@ -53,6 +53,7 @@ #define NR_IOSAPICS 256 +#ifndef CONFIG_XEN static inline unsigned int iosapic_read(char __iomem *iosapic, unsigned int reg) { writel(reg, iosapic + IOSAPIC_REG_SELECT); @@ -64,6 +65,7 @@ static inline void iosapic_write(char __iomem *iosapic, unsigned int reg, u32 va writel(reg, iosapic + IOSAPIC_REG_SELECT); writel(val, iosapic + IOSAPIC_WINDOW); } +#endif static inline void iosapic_eoi(char __iomem *iosapic, u32 vector) { diff --git a/xen/arch/ia64/linux-xen/iosapic.c b/xen/arch/ia64/linux-xen/iosapic.c index 9d964abba4..e5639ffe7f 100644 --- a/xen/arch/ia64/linux-xen/iosapic.c +++ b/xen/arch/ia64/linux-xen/iosapic.c @@ -1118,3 +1118,114 @@ static int __init iosapic_enable_kmalloc (void) return 0; } core_initcall (iosapic_enable_kmalloc); + +#ifdef XEN +/* nop for now */ +void set_irq_affinity_info(unsigned int irq, int hwid, int redir) {} + +static int iosapic_physbase_to_id(unsigned long physbase) +{ + int i; + unsigned long addr = physbase | __IA64_UNCACHED_OFFSET; + + for (i = 0; i < NR_IOSAPICS; i++) { + if ((unsigned long)(iosapic_lists[i].addr) == addr) + return i; + } + + return -1; +} + +int iosapic_guest_read(unsigned long physbase, unsigned int reg, u32 *pval) +{ + int id; + unsigned long flags; + + if ((id = (iosapic_physbase_to_id(physbase))) < 0) + return id; + + spin_lock_irqsave(&iosapic_lock, flags); + *pval = iosapic_read(iosapic_lists[id].addr, reg); + spin_unlock_irqrestore(&iosapic_lock, flags); + + return 0; +} + +int iosapic_guest_write(unsigned long physbase, unsigned int reg, u32 val) +{ + unsigned int id, gsi, vec, dest, high32; + char rte_index; + struct iosapic *ios; + struct iosapic_intr_info *info; + struct rte_entry rte; + unsigned long flags; + + if ((id = (iosapic_physbase_to_id(physbase))) < 0) + return -EINVAL; + ios = &iosapic_lists[id]; + + /* Only handle first half of RTE update */ + if ((reg < 0x10) || (reg & 1)) + return 0; + + rte.val = val; + rte_index = IOSAPIC_RTEINDEX(reg); + vec = rte.lo.vector; +#if 0 + /* Take PMI/NMI/INIT/EXTINT handled by xen */ + if (rte.delivery_mode > IOSAPIC_LOWEST_PRIORITY) { + printk("Attempt to write IOSAPIC dest mode owned by xen!\n"); + printk("IOSAPIC/PIN = (%d/%d), lo = 0x%x\n", + id, rte_index, val); + return -EINVAL; + } +#endif + + /* Sanity check. Vector should be allocated before this update */ + if ((rte_index > ios->num_rte) || + ((vec > IA64_FIRST_DEVICE_VECTOR) && + (vec < IA64_LAST_DEVICE_VECTOR) && + (!test_bit(vec - IA64_FIRST_DEVICE_VECTOR, ia64_vector_mask)))) + return -EINVAL; + + gsi = ios->gsi_base + rte_index; + info = &iosapic_intr_info[vec]; + spin_lock_irqsave(&irq_descp(vec)->lock, flags); + spin_lock(&iosapic_lock); + if (!gsi_vector_to_rte(gsi, vec)) { + register_intr(gsi, vec, IOSAPIC_LOWEST_PRIORITY, + rte.lo.polarity, rte.lo.trigger); + } else if (vector_is_shared(vec)) { + if ((info->trigger != rte.lo.trigger) || + (info->polarity != rte.lo.polarity)) { + printk("WARN: can't override shared interrupt vec\n"); + printk("IOSAPIC/PIN = (%d/%d), ori = 0x%x, new = 0x%x\n", + id, rte_index, info->low32, rte.val); + spin_unlock(&iosapic_lock); + spin_unlock_irqrestore(&irq_descp(vec)->lock, flags); + return -EINVAL; + } + + /* If the vector is shared and already unmasked for other + * interrupt sources, don't mask it. + * + * Same check may also apply to single gsi pin, which may + * be shared by devices belonging to different domain. But + * let's see how to act later on demand. + */ + if (!(info->low32 & IOSAPIC_MASK)) + rte.lo.mask = 0; + } + + /* time to update physical RTE */ + dest = cpu_physical_id(smp_processor_id()); + high32 = (dest << IOSAPIC_DEST_SHIFT); + iosapic_write(iosapic_lists[id].addr, reg + 1, high32); + iosapic_write(iosapic_lists[id].addr, reg, rte.val); + info->low32 = rte.val; + info->dest = dest; + spin_unlock(&iosapic_lock); + spin_unlock_irqrestore(&irq_descp(vec)->lock, flags); + return 0; +} +#endif /* XEN */ diff --git a/xen/arch/ia64/linux-xen/irq_ia64.c b/xen/arch/ia64/linux-xen/irq_ia64.c index 94c437bc64..d16b38e539 100644 --- a/xen/arch/ia64/linux-xen/irq_ia64.c +++ b/xen/arch/ia64/linux-xen/irq_ia64.c @@ -60,7 +60,11 @@ __u8 isa_irq_to_vector_map[16] = { }; EXPORT_SYMBOL(isa_irq_to_vector_map); +#ifdef XEN +unsigned long ia64_vector_mask[BITS_TO_LONGS(IA64_NUM_DEVICE_VECTORS)]; +#else static unsigned long ia64_vector_mask[BITS_TO_LONGS(IA64_NUM_DEVICE_VECTORS)]; +#endif int assign_irq_vector (int irq) diff --git a/xen/arch/ia64/xen/acpi.c b/xen/arch/ia64/xen/acpi.c index b75bdaefae..449124820a 100644 --- a/xen/arch/ia64/xen/acpi.c +++ b/xen/arch/ia64/xen/acpi.c @@ -44,7 +44,7 @@ #include #include #include -//#include +#include #include #include #include @@ -121,9 +121,7 @@ acpi_get_sysname (void) #ifdef CONFIG_ACPI_BOOT #define ACPI_MAX_PLATFORM_INTERRUPTS 256 -#define NR_IOSAPICS 4 -#if 0 /* Array to record platform interrupt vectors for generic interrupt routing. */ int platform_intr_list[ACPI_MAX_PLATFORM_INTERRUPTS] = { [0 ... ACPI_MAX_PLATFORM_INTERRUPTS - 1] = -1 @@ -147,7 +145,7 @@ acpi_request_vector (u32 int_type) printk(KERN_ERR "acpi_request_vector(): invalid interrupt type\n"); return vector; } -#endif + char * __acpi_map_table (unsigned long phys_addr, unsigned long size) { @@ -253,9 +251,7 @@ acpi_parse_iosapic (acpi_table_entry_header *header, const unsigned long end) acpi_table_print_madt_entry(header); -#if 0 iosapic_init(iosapic->address, iosapic->global_irq_base); -#endif return 0; } @@ -265,9 +261,7 @@ acpi_parse_plat_int_src ( acpi_table_entry_header *header, const unsigned long end) { struct acpi_table_plat_int_src *plintsrc; -#if 0 int vector; -#endif plintsrc = (struct acpi_table_plat_int_src *) header; @@ -276,7 +270,6 @@ acpi_parse_plat_int_src ( acpi_table_print_madt_entry(header); -#if 0 /* * Get vector assignment for this interrupt, set attributes, * and program the IOSAPIC routing table. @@ -290,7 +283,6 @@ acpi_parse_plat_int_src ( (plintsrc->flags.trigger == 1) ? IOSAPIC_EDGE : IOSAPIC_LEVEL); platform_intr_list[plintsrc->type] = vector; -#endif return 0; } @@ -308,11 +300,9 @@ acpi_parse_int_src_ovr ( acpi_table_print_madt_entry(header); -#if 0 iosapic_override_isa_irq(p->bus_irq, p->global_irq, (p->flags.polarity == 1) ? IOSAPIC_POL_HIGH : IOSAPIC_POL_LOW, (p->flags.trigger == 1) ? IOSAPIC_EDGE : IOSAPIC_LEVEL); -#endif return 0; } @@ -364,9 +354,7 @@ acpi_parse_madt (unsigned long phys_addr, unsigned long size) #else has_8259 = acpi_madt->flags.pcat_compat; #endif -#if 0 iosapic_system_init(has_8259); -#endif /* Get base address of IPI Message Block */ diff --git a/xen/arch/ia64/xen/hypercall.c b/xen/arch/ia64/xen/hypercall.c index 22d2a2fb41..f0696cdbe7 100644 --- a/xen/arch/ia64/xen/hypercall.c +++ b/xen/arch/ia64/xen/hypercall.c @@ -21,8 +21,12 @@ #include #include #include +#include +#include +#include extern unsigned long translate_domain_mpaddr(unsigned long); +static long do_physdev_op(GUEST_HANDLE(physdev_op_t) uop); /* FIXME: where these declarations should be there ? */ extern int dump_privop_counts_to_user(char *, int); extern int zero_privop_counts_to_user(char *, int); @@ -51,7 +55,7 @@ hypercall_t ia64_hypercall_table[] = (hypercall_t)do_event_channel_op, (hypercall_t)do_xen_version, (hypercall_t)do_console_io, - (hypercall_t)do_ni_hypercall, /* do_physdev_op */ + (hypercall_t)do_physdev_op, /* do_physdev_op */ (hypercall_t)do_grant_table_op, /* 20 */ (hypercall_t)do_ni_hypercall, /* do_vm_assist */ (hypercall_t)do_ni_hypercall, /* do_update_va_mapping_otherdomain */ @@ -89,6 +93,11 @@ xen_hypercall (struct pt_regs *regs) regs->r8 = do_event_channel_op(guest_handle_from_ptr(regs->r14, evtchn_op_t)); break; + case __HYPERVISOR_physdev_op: + regs->r8 = do_physdev_op(guest_handle_from_ptr(regs->r14, + physdev_op_t)); + break; + case __HYPERVISOR_grant_table_op: regs->r8 = do_grant_table_op((unsigned int) regs->r14, guest_handle_from_ptr(regs->r15, void), @@ -282,3 +291,80 @@ ia64_hypercall (struct pt_regs *regs) else return xen_hypercall (regs); } + +/* Need make this function common */ +extern int +iosapic_guest_read( + unsigned long physbase, unsigned int reg, u32 *pval); +extern int +iosapic_guest_write( + unsigned long physbase, unsigned int reg, u32 pval); + +static long do_physdev_op(GUEST_HANDLE(physdev_op_t) uop) +{ + struct physdev_op op; + long ret; + int irq; + + if ( unlikely(copy_from_guest(&op, uop, 1) != 0) ) + return -EFAULT; + + switch ( op.cmd ) + { + case PHYSDEVOP_IRQ_UNMASK_NOTIFY: + ret = pirq_guest_unmask(current->domain); + break; + + case PHYSDEVOP_IRQ_STATUS_QUERY: + irq = op.u.irq_status_query.irq; + ret = -EINVAL; + if ( (irq < 0) || (irq >= NR_IRQS) ) + break; + op.u.irq_status_query.flags = 0; + /* Edge-triggered interrupts don't need an explicit unmask downcall. */ + if ( !strstr(irq_desc[irq_to_vector(irq)].handler->typename, "edge") ) + op.u.irq_status_query.flags |= PHYSDEVOP_IRQ_NEEDS_UNMASK_NOTIFY; + ret = 0; + break; + + case PHYSDEVOP_APIC_READ: + ret = -EPERM; + if ( !IS_PRIV(current->domain) ) + break; + ret = iosapic_guest_read( + op.u.apic_op.apic_physbase, + op.u.apic_op.reg, + &op.u.apic_op.value); + break; + + case PHYSDEVOP_APIC_WRITE: + ret = -EPERM; + if ( !IS_PRIV(current->domain) ) + break; + ret = iosapic_guest_write( + op.u.apic_op.apic_physbase, + op.u.apic_op.reg, + op.u.apic_op.value); + break; + + case PHYSDEVOP_ASSIGN_VECTOR: + if ( !IS_PRIV(current->domain) ) + return -EPERM; + + if ( (irq = op.u.irq_op.irq) >= NR_IRQS ) + return -EINVAL; + + op.u.irq_op.vector = assign_irq_vector(irq); + ret = 0; + break; + + default: + ret = -EINVAL; + break; + } + + if ( copy_to_guest(uop, &op, 1) ) + ret = -EFAULT; + + return ret; +} diff --git a/xen/arch/ia64/xen/irq.c b/xen/arch/ia64/xen/irq.c index cdf97eb03e..5fbffdf82b 100644 --- a/xen/arch/ia64/xen/irq.c +++ b/xen/arch/ia64/xen/irq.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include @@ -44,11 +43,10 @@ #include #include #include -#include +#include +#include #include -#define _irq_desc irq_desc -#define irq_descp(irq) &irq_desc[irq] #define apicid_to_phys_cpu_present(x) 1 /* @@ -70,7 +68,7 @@ /* * Controller mappings for all interrupt sources: */ -irq_desc_t _irq_desc[NR_IRQS] __cacheline_aligned = { +irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = { [0 ... NR_IRQS-1] = { .status = IRQ_DISABLED | IRQ_GUEST, .handler = &no_irq_type, diff --git a/xen/include/asm-ia64/linux-xen/asm/iosapic.h b/xen/include/asm-ia64/linux-xen/asm/iosapic.h index ab6c584a75..55e559b2b8 100644 --- a/xen/include/asm-ia64/linux-xen/asm/iosapic.h +++ b/xen/include/asm-ia64/linux-xen/asm/iosapic.h @@ -134,10 +134,30 @@ static inline void list_move(struct list_head *list, struct list_head *head) #undef nop #endif -/* nop for now */ -static inline void -set_irq_affinity_info(unsigned int irq, int hwid, int redir) {} - +struct rte_entry { + union { + struct { + u32 vector : 8, + delivery_mode : 3, + dest_mode : 1, /* always 0 for iosapic */ + delivery_status : 1, + polarity : 1, + __reserved0 : 1, + trigger : 1, + mask : 1, + __reserved1 : 15; + } lo; + struct { + u32 __reserved2 : 16, + eid : 8, + id : 8; + } hi; + u32 val; + }; +}; + +#define IOSAPIC_RTEINDEX(reg) (((reg) - 0x10) >> 1) +extern unsigned long ia64_vector_mask[]; #endif /* XEN */ # endif /* !__ASSEMBLY__ */ -- 2.30.2